﻿/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_MATH_H__
#define __CTL_MATH_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_DEF_H__
	#error math.h requires def.h to be included first.
#endif

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

//Macros

//SQUARE(x)
/*! \brief Calculate the square.

Calculate the square.
*/
#define SQUARE(x)  ((x) * (x))
//CUBE(x)
/*! \brief Calculate the cube.

Calculate the cube.
*/
#define CUBE(x)    ((x) * (x) * (x))
//QUAD(x)
/*! \brief Calculate the quad.

Calculate the quad.
*/
#define QUAD(x)    ((x) * (x) * (x) * (x))

//Classes

//Complex<T>
/*! \brief A class for complex number.

A class for complex number.
\tparam T The POD type name. It can be double, float, etc.
*/
template <typename T>
class Complex
{
public:
	typedef T  ValueType;

public:
	/*! \brief Constructor.

	Constructor.
	*/
	Complex() noexcept
	{
	}
	/*! \brief Constructor.

	Constructor.
	\param r [in] The real component.
	\param i [in] The imaginary component.
	*/
	Complex(const T& r, const T& i) noexcept : m_real(r), m_imag(i)
	{
	}
	/*! \brief Copy constructor.

	Copy constructor.
	*/
	Complex(const Complex<T>& src) noexcept : m_real(src.m_real), m_imag(src.m_imag)
	{
	}
	template <typename U>
	Complex(const Complex<U>& src) noexcept : m_real(src.m_real), m_imag(src.m_imag)
	{
	}
	~Complex() noexcept
	{
	}

	/*! \brief Assignment operator.

	Assignment operator.
	*/
	Complex<T>& operator=(const Complex<T>& src) noexcept
	{
		m_real = src.m_real;
		m_imag = src.m_imag;
		return *this;
	}
	template <typename U>
	Complex<T>& operator=(const Complex<U>& src) noexcept
	{
		m_real = src.m_real;
		m_imag = src.m_imag;
		return *this;
	}

	/*! \brief Get real component.

	Get real component.
	\return A reference of T.
	*/
	const T& GetReal() const noexcept
	{
		return m_real;
	}
	T& GetReal() noexcept
	{
		return m_real;
	}
	/*! \brief Get imaginary component.

	Get imaginary component.
	\return A reference of T.
	*/
	const T& GetImag() const noexcept
	{
		return m_imag;
	}
	T& GetImag() noexcept
	{
		return m_imag;
	}

	/*! \brief Set a value.

	Set a value.
	\param r [in] The real component.
	\param i [in] The imaginary component.
	*/
	void Set(const T& r, const T& i) noexcept
	{
		m_real = r;
		m_imag = i;
	}

	T Abs() const noexcept
	{
		return (T)FloatTraits<T>::sqrt(Norm());
	}
	/*! \brief Get the angle of this complex number.

	Get the angle of this complex number.
	\return The angle value.
	*/
	T Arg() const noexcept
	{
		return (T)FloatTraits<T>::atan2(m_imag, m_real);
	}
	/*! \brief Calculate the norm of this complex number.

	Calculate the norm of this complex number.
	\return The squared magnitude.
	*/
	T Norm() const noexcept
	{
		return SQUARE(m_real) + SQUARE(m_imag);
	}
	/*! \brief Calculate the conjugation of this complex number.

	Calculate the conjugation of this complex number.
	\return A reference of this object.
	*/
	Complex<T>& Conj() noexcept
	{
		m_imag = -m_imag;
		return *this;
	}

//operators

	/*! \brief Set value from polar.

	Set value from polar.
	\param rho [in] The polar value.
	\param theta [in] The polar angle.
	\return A reference of this object.
	\note This complex number will be rho * exp(i * theta).
	*/
	Complex<T>& Polar(const T& rho, const T& theta) noexcept
	{
		m_real = (T)(rho * FloatTraits<T>::cos(theta));
		m_imag = (T)(rho * FloatTraits<T>::sin(theta));
		return *this;
	}

	/*! \brief Add self operator.

	Add self operator.
	*/
	Complex<T>& operator+=(const T& v) noexcept
	{
		m_real += v;
		m_imag += v;
		return *this;
	}
	Complex<T>& operator+=(const Complex<T>& v) noexcept
	{
		m_real += v.m_real;
		m_imag += v.m_imag;
		return *this;
	}
	template <typename U>
	Complex<T>& operator+=(const Complex<U>& v) noexcept
	{
		m_real += v.m_real;
		m_imag += v.m_imag;
		return *this;
	}

	/*! \brief Subtract self operator.

	Subtract self operator.
	*/
	Complex<T>& operator-=(const T& v) noexcept
	{
		m_real -= v;
		m_imag -= v;
		return *this;
	}
	Complex<T>& operator-=(const Complex<T>& v) noexcept
	{
		m_real -= v.m_real;
		m_imag -= v.m_imag;
		return *this;
	}
	template <typename U>
	Complex<T>& operator-=(const Complex<U>& v) noexcept
	{
		m_real -= v.m_real;
		m_imag -= v.m_imag;
		return *this;
	}

	/*! \brief Multiply self operator.

	Multiply self operator.
	*/
	Complex<T>& operator*=(const T& v) noexcept
	{
		m_real *= v;
		m_imag *= v;
		return *this;
	}
	Complex<T>& operator*=(const Complex<T>& v) noexcept
	{
		T tmp = m_real * v.m_real - m_imag * v.m_imag;
		m_imag = m_real * v.m_imag + m_imag * v.m_real;
		m_real = tmp;
		return *this;
	}
	template <typename U>
	Complex<T>& operator*=(const Complex<U>& v) noexcept
	{
		T tmp = m_real * v.m_real - m_imag * v.m_imag;
		m_imag = m_real * v.m_imag + m_imag * v.m_real;
		m_real = tmp;
		return *this;
	}

	/*! \brief Divide self operator.

	Divide self operator.
	*/
	Complex<T>& operator/=(const T& v) noexcept
	{
		m_real /= v;
		m_imag /= v;
		return *this;
	}
	Complex<T>& operator/=(const Complex<T>& v) noexcept
	{
		T n = v.Norm();
		T tmp = (m_real * v.m_real + m_imag * v.m_imag) / n;
		m_imag = (v.m_real * m_imag - m_real * v.m_imag) / n;
		m_real = tmp;
		return *this;
	}
	template <typename U>
	Complex<T>& operator/=(const Complex<U>& v) noexcept
	{
		T n = v.Norm();
		T tmp = (m_real * v.m_real + m_imag * v.m_imag) / n;
		m_imag = (v.m_real * m_imag - m_real * v.m_imag) / n;
		m_real = tmp;
		return *this;
	}

	/*! \brief Add operator.

	Add operator.
	*/
	Complex<T> operator+(const T& v) const noexcept
	{
		Complex<T> ret(*this);
		ret += v;
		return ret;
	}
	Complex<T> operator+(const Complex<T>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret += v;
		return ret;
	}
	template <typename U>
	Complex<T> operator+(const Complex<U>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret += v;
		return ret;
	}

	/*! \brief Subtract operator.

	Subtract operator.
	*/
	Complex<T> operator-(const T& v) const noexcept
	{
		Complex<T> ret(*this);
		ret -= v;
		return ret;
	}
	Complex<T> operator-(const Complex<T>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret -= v;
		return ret;
	}
	template <typename U>
	Complex<T> operator-(const Complex<U>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret -= v;
		return ret;
	}

	/*! \brief Multiplication operator.

	Multiplication operator.
	*/
	Complex<T> operator*(const T& v) const noexcept
	{
		Complex<T> ret(*this);
		ret *= v;
		return ret;
	}
	Complex<T> operator*(const Complex<T>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret *= v;
		return ret;
	}
	template <typename U>
	Complex<T> operator*(const Complex<U>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret *= v;
		return ret;
	}

	/*! \brief Division operator.

	Division operator.
	*/
	Complex<T> operator/(const T& v) const noexcept
	{
		Complex<T> ret(*this);
		ret /= v;
		return ret;
	}
	Complex<T> operator/(const Complex<T>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret /= v;
		return ret;
	}
	template <typename U>
	Complex<T> operator/(const Complex<U>& v) const noexcept
	{
		Complex<T> ret(*this);
		ret /= v;
		return ret;
	}

	/*! \brief Negative operator.

	Negative operator.
	*/
	Complex<T> operator-() const noexcept
	{
		return Complex<T>(-m_real, -m_imag);
	}

private:
	T m_real;  //!< real component
	T m_imag;  //!< imaginary component

private:
	template <class U, class TTrait>
	friend class ComplexCompareTrait;
};

//ComplexCompareTrait<T, TTrait>

template <class T, class TTrait = CTL::DefaultCompareTrait<typename T::ValueType>>
class ComplexCompareTrait
{
public:
	typedef T  ValueType;
	typedef TTrait  TraitType;

public:
	static bool IsEQ(const ValueType& t1, const ValueType& t2) noexcept
	{
		return TraitType::IsEQ(t1.m_real, t2.m_real) && TraitType::IsEQ(t1.m_imag, t2.m_imag);
	}
	static bool IsNE(const ValueType& t1, const ValueType& t2) noexcept
	{
		return TraitType::IsNE(t1.m_real, t2.m_real) || TraitType::IsNE(t1.m_imag, t2.m_imag);
	}
};

#pragma pack(push, 1)

//Vec<T, t_uSize>
//  T: POD type

template <typename T, uintptr_t t_uSize>
class Vec
{
public:
	static constexpr const uintptr_t c_uSize = t_uSize;

public:
	//default constructor, destructor, assignment

	const T* GetPointer() const noexcept
	{
		return m_data;
	}
	T* GetPointer() noexcept
	{
		return m_data;
	}

	const T& GetAt(uintptr_t index) const noexcept
	{
		assert( index < c_uSize );
		return m_data[index];
	}
	T& GetAt(uintptr_t index) noexcept
	{
		assert( index < c_uSize );
		return m_data[index];
	}

	const T& operator[](uintptr_t index) const noexcept
	{
		return GetAt(index);
	}
	T& operator[](uintptr_t index) noexcept
	{
		return GetAt(index);
	}

private:
	T m_data[t_uSize];
};

//Mat<T, t_uCols, t_uRows>
//  T: POD type
//  column-major order

template <typename T, uintptr_t t_uCols, uintptr_t t_uRows>
class Mat
{
public:
	typedef T  ValueType;
	typedef Vec<T, t_uRows>  ColumnType;

	static constexpr const uintptr_t c_uCols = t_uCols;
	static constexpr const uintptr_t c_uRows = t_uRows;

public:
	//default constructor, destructor, assignment

	const T* GetPointer() const noexcept
	{
		return m_cols[0].GetPointer();
	}
	T* GetPointer() noexcept
	{
		return m_cols[0].GetPointer();
	}

	const ColumnType& GetColumnAt(uintptr_t uColumn) const noexcept
	{
		assert( uColumn < c_uCols );
		return m_cols[uColumn];
	}
	ColumnType& GetColumnAt(uintptr_t uColumn) noexcept
	{
		assert( uColumn < c_uCols );
		return m_cols[uColumn];
	}

	const ColumnType& operator[](uintptr_t uColumn) const noexcept
	{
		return GetColumnAt(uColumn);
	}
	ColumnType& operator[](uintptr_t uColumn) noexcept
	{
		return GetColumnAt(uColumn);
	}

private:
	ColumnType m_cols[t_uCols];
};

#pragma pack(pop)

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
